/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef HMFS_RESIZE_DATA_H
#define HMFS_RESIZE_DATA_H

#include <cassert>
#include <cstdlib>
#include <unistd.h>
#include <cstdio>
#include <stdbool.h>
#include <stddef.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/utsname.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif

#include "hmfs_define.h"
#include "resize_list.h"
#include "device_manager.h"

namespace OHOS {
namespace Hmfs {

#define EXIT_ERR_CODE        (-1)
#define SUCCESS_CODE        (0)
template<typename T>
bool ver_after(const T& a, const T& b)
{
    return (a > b);
}

#define nats_in_cursum(jnl)             (LE16_TO_NATIVE(jnl->nNats))
#define sits_in_cursum(jnl)             (LE16_TO_NATIVE(jnl->nSits))

#define nat_in_journal(jnl, i)          (jnl->natJ.entries[i].ne)
#define nid_in_journal(jnl, i)          (jnl->natJ.entries[i].nid)
#define sit_in_journal(jnl, i)          (jnl->sitJ.entries[i].se)
#define segno_in_journal(jnl, i)        (jnl->sitJ.entries[i].segno)
/*
 * indicate meta/data type
 */
enum {
    META_CP,
    META_NAT,
    META_SIT,
    META_SSA,
    META_MAX,
    META_POR,
};

#define MAX_RA_BLOCKS    64

enum {
    NAT_BITMAP,
    SIT_BITMAP
};

enum {
    LFS = 0,
    SSR
};

struct NodeInfo {
    uint32_t nid;
    uint32_t ino;
    uint32_t blockAddr;
    unsigned char version;
};

/** zzx update */
enum SB_ADDR {
    SB0_ADDR = 0,
    SB1_ADDR,
    SB_MAX_ADDR,
};

#define DEVICE_NUM         1

struct HmfsConfigData {
    /** init param */
    int nDevices;
    uint32_t sectorsPerBlock;
    uint32_t blocksPerSegment;
    uint64_t wantedTotalSectors;
    uint64_t wantedSectorSize;
    int preserveLimits;
    int noKernelCheck;
    struct DeviceInfo devices[MAX_DEVICES];
    uint32_t segmentsPerSection;
    uint32_t sectionsPerZone;
    uint32_t segmentsPerZone;
    char *volumeLabel;
    int trim;
    int32_t kd;
    time_t fixedTime;
    uint16_t fileEncoding;
    uint16_t fileEncodingFlags;
    uint32_t rootUid;
    uint32_t rootGid;
    /*--------------*/
    int debugLevel;
    int force;
    int safeResize;
    uint64_t targetSectors;
    uint64_t totalSectors;
    int largeNatBitmap;
    uint32_t feature;
    double newOverprovision;
    /** init param */
    uint8_t initVersion[VERSION_LEN + 1];
    uint8_t sbVersion[VERSION_LEN + 1];
    uint8_t version[VERSION_LEN + 1];
    int sparseMode;
    int func;
    int forceStop;
    int abnormalStop;
    int32_t hmfsErrors;
    int quotaFix;
    uint32_t newReservedSegments;
    /* precomputed fs UUID checksum for seeding other checksums */
    uint32_t chksumSeed;
    int zonedModel;
    uint32_t sectorSize;
    /** useful param */
    int bugOn;
    int fixOn;
    uint32_t reservedSegments;
    int zonedMode;
    uint32_t startSector;
    size_t zoneBlocks;
    double overprovision;
    uint32_t curSeg[6];
    uint32_t totalSegments;
    int32_t autoFix;
    int32_t preenMode;
    uint16_t encoding;
    uint16_t encodingFlags;
    int32_t layout;
};


#define BLOCK_SZ        4096
struct block {
    unsigned char buf[BLOCK_SZ];
};

#define ASSERT(exp)                            \
    do {                                \
        if (!(exp)) {                        \
            printf("[ASSERT] (%s:%4d) %s\n",        \
                    __func__, __LINE__, #exp);    \
            exit(-1);                    \
        }                            \
    } while (0)

#define SB_MASK(i)    (1 << (i))
#define SB_MASK_ALL    (SB_MASK(SB0_ADDR) | SB_MASK(SB1_ADDR))
#define SB_MASK_ONE    (SB_MASK(SB0_ADDR))

enum seg_type {
    SEG_TYPE_DATA,
    SEG_TYPE_CUR_DATA,
    SEG_TYPE_NODE,
    SEG_TYPE_CUR_NODE,
    SEG_TYPE_MAX,
};

/* summary block type, node or data, is stored to the SummaryFooter */
#define IS_SUM_NODE_SEG(footer)    (footer.entryType == SUM_TYPE_NODE)
#define IS_SUM_DATA_SEG(footer)    (footer.entryType == SUM_TYPE_DATA)
struct NodeAddressTable {
    uint32_t natBlkaddr;
    uint32_t natBlocks;
    uint32_t maxNid;
    uint32_t initScanNid;
    uint32_t nextScanNid;

    unsigned int natCnt;
    unsigned int fcnt;

    char *natBitmap;
    int bitmapSize;
    char *nidBitmap;
};

struct SegEntry {
    unsigned short validBlocks;    /* # of valid blocks */
    unsigned short ckptValidBlocks;    /* # of valid blocks last cp, for recovered data/node */
    unsigned char *curValidBitmap;   /* validity bitmap of blocks */
    unsigned char *ckptValidBitmap;    /* validity bitmap of blocks last cp, for recovered data/node */
    unsigned char type;             /* segment type like CURSEG_XXX_TYPE */
    unsigned char origType;        /* segment type like CURSEG_XXX_TYPE */
    unsigned char ckptType;        /* segment type like CURSEG_XXX_TYPE , for recovered data/node */
    unsigned long long modTime;       /* modification time of the segment */
    int dirty;
};

struct SecEntry {
    unsigned int validBlocks;      /* # of valid blocks in a section */
};

struct SitInfo {

    uint32_t sitBaseAddr;          /* start block address of SIT area */
    uint32_t sitBlocks;             /* # of blocks used by SIT area */
    uint32_t writtenValidBlocks;   /* # of valid blocks in main area */
    unsigned char *bitmap;        /* all bitmaps pointer */
    char *sitBitmap;               /* SIT bitmap pointer */
    unsigned int bitmapSize;       /* SIT bitmap size */

    unsigned long *dirtySentriesBitmap;   /* bitmap for dirty sentries */
    unsigned int dirtySentries;            /* # of dirty sentries */
    unsigned int sentsPerBlock;           /* # of SIT entries per block */
    struct SegEntry *segEntries;             /* SIT segment-level cache */
    struct SecEntry *secEntries;          /* SIT section-level cache */

    unsigned long long elapsedTime;        /* elapsed time after mount */
    unsigned long long mountedTime;        /* mount time */
    unsigned long long minModtime;           /* min. modification time */
    unsigned long long maxModtime;           /* max. modification time */
};


/* summary block type, node or data, is stored to the SummaryFooter */
#define SUM_TYPE_NODE        (1)
#define SUM_TYPE_DATA        (0)
#define GET_SUM_BLKADDR(sbi, segno)                    \
    ((sbi->smInfoTable->ssaBlkaddr) + segno)

/*
 * For SIT entries
 *
 * Each segment is 2MB in size by default so that a bitmap for validity of
 * there-in blocks should occupy 64 bytes, 512 bits.
 * Not allow to change this.
 */
// #define SIT_VBLOCK_MAP_SIZE 64

struct CurSegmentInfo {
    struct SummaryBlockData *segSumBlk;     /* cached summary block */
    unsigned char allocType;               /* current allocation type */
    unsigned int segNum;                     /* current segment number */
    unsigned short nextBlkOffset;             /* next block offset to write */
    unsigned int zone;                      /* current zone number */
    unsigned int nextSegNum;                /* preallocated segment */
};

struct SegmentInfoTable {
    struct SitInfo *sitInfo;
    struct CurSegmentInfo *curSegmentArray;

    uint32_t seg0Blkaddr;
    uint32_t mainBlkaddr;
    uint32_t ssaBlkaddr;

    unsigned int segmentCount;
    unsigned int mainSegments;
    unsigned int reservedSegments;
    unsigned int ovpSegments;
};


struct HmfsSbInfo {
    // struct f2fs_fsck *fsck;
    struct SuperBlockData *rawSuper; //raw_super;
    struct CheckPointData *ckptData;
    struct SegmentInfoTable *smInfoTable;
    struct NodeAddressTable *naTable;

    int curCpId;

    struct ListNode orphan_inode_list;
    unsigned int n_orphans;
    /* basic file system units */
    unsigned int logSectorsPerBlk;     /* log2 sectors per block */
    unsigned int logBlockSize;             /* log2 block size */
    unsigned int blockSize;                 /* block size */
    unsigned int rootInodeId;              /* root inode number*/
    unsigned int nodeInodeId;              /* node inode number*/
    unsigned int metaInodeId;              /* meta inode number*/
    unsigned int logBlksPerSeg;        /* log2 blocks per segment */
    unsigned int blocksPerSeg;            /* blocks per segment */
    unsigned int segmentsPerSec;              /* segments per section */
    unsigned int sectionsPerZone;             /* sections per zone */
    unsigned int totalSections;            /* total section count */
    unsigned int totalNodeCount;          /* total node block count */
    unsigned int totalValidNodeCount;    /* valid node block count */
    unsigned int totalValidInodeCount;   /* valid inode count */
    int activeLogs; //active_logs;                        /* # of active logs */

    uint32_t userBlockCount;               /* # of user blocks */
    uint32_t totalValidBlockCount;        /* # of valid blocks */
    uint32_t allocValidBlockCount;        /* # of allocated blocks */
    uint32_t lastValidBlockCount;         /* for recovery */
    uint32_t s_next_generation;                  /* for NFS support */
    unsigned int curVictimSec;            /* current victim section num */
    uint32_t free_segments;
    int cpBackuped;            /* backup valid checkpoint */
    /* true if late_build_segment_manger() is called */
    bool segManagerDone;
    /* keep track of hardlinks so we can recreate them */
    void *hardlink_cache;
};

inline struct SuperBlockData *F2FS_RAW_SUPER(std::shared_ptr<HmfsSbInfo> sbi)
{
    if (sbi == nullptr) {
        return nullptr;
    }
    return (struct SuperBlockData *)(sbi->rawSuper);
}

inline struct CheckPointData *F2FS_CKPT(std::shared_ptr<HmfsSbInfo> sbi)
{
    if (sbi == nullptr) {
        return nullptr;
    }
    return (struct CheckPointData *)(sbi->ckptData);
}

inline struct NodeAddressTable *NM_I(std::shared_ptr<HmfsSbInfo> sbi)
{
    if (sbi == nullptr) {
        return nullptr;
    }
    return (struct NodeAddressTable *)(sbi->naTable);
}

inline struct SegmentInfoTable *SM_I(std::shared_ptr<HmfsSbInfo> sbi)
{
    if (sbi == nullptr) {
        return nullptr;
    }
    return (struct SegmentInfoTable *)(sbi->smInfoTable);
}

inline struct SitInfo *SIT_I(std::shared_ptr<HmfsSbInfo> sbi)
{
    if (sbi == nullptr) {
        return nullptr;
    }
    return (struct SitInfo *)(SM_I(sbi)->sitInfo);
}

inline bool IsSetCkptFlags(struct CheckPointData *cp, unsigned int f)
{
    if (cp == nullptr) {
        return false;
    }
    unsigned int ckptFlags = LE32_TO_NATIVE(cp->cpFlags);
    return ckptFlags & f ? 1 : 0;
}

inline unsigned long GetBitmapSize(std::shared_ptr<HmfsSbInfo> sbi, int flag)
{
    CheckPointData *cpData = sbi->ckptData;
    /* return NAT or SIT bitmap */
    if (flag == NAT_BITMAP) {
        return LE32_TO_NATIVE(cpData->natVersionBitmapSize);
    } else if (flag == SIT_BITMAP) {
        return LE32_TO_NATIVE(cpData->sitVersionBitmapSize);
    }
    return 0;
}

/*
 * For NAT entries
 */
inline bool IsValidNid(std::shared_ptr<HmfsSbInfo> sbi, uint32_t nid)
{
    return (nid < (NAT_ENTRY_PER_BLOCK * LE32_TO_NATIVE(sbi->rawSuper->segmentCountInNAT) <<
        (sbi->logBlksPerSeg - 1)));
}

inline bool IsValidBlkAddr(std::shared_ptr<HmfsSbInfo> sbi, uint32_t addr)
{
    if (addr == NULL_ADDR || addr == NEW_ADDR) {
        return 1;
    }

    if (addr >= LE64_TO_NATIVE(sbi->rawSuper->blockCount) || addr < sbi->smInfoTable->mainBlkaddr) {
        DBG(1, "block addr [0x%x]\n", addr);
        return 0;
    }
    /* next block offset will be checked at the end of fsck. */
    return 1;
}

#define BLOCK_SZ    4096

#define IS_DATASEG(t)                                                   \
    ((t == CURSEG_HOT_DATA) || (t == CURSEG_COLD_DATA) ||           \
     (t == CURSEG_WARM_DATA))

#define IS_NODESEG(t)                                                   \
    ((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) ||           \
     (t == CURSEG_WARM_NODE))

#define MAIN_BLKADDR(sbi)                        \
    (SM_I(sbi) ? SM_I(sbi)->mainBlkaddr :                \
        LE32_TO_NATIVE(F2FS_RAW_SUPER(sbi)->mainBlkId))
#define SEG0_BLKADDR(sbi)                        \
    (SM_I(sbi) ? SM_I(sbi)->seg0Blkaddr :                \
        LE32_TO_NATIVE(F2FS_RAW_SUPER(sbi)->segment0BlkId))

#define MAIN_SEGS(sbi)    (SM_I(sbi)->mainSegments)
#define TOTAL_SEGS(sbi)    (SM_I(sbi)->segmentCount)
#define TOTAL_BLKS(sbi)    (TOTAL_SEGS(sbi) << (sbi)->logBlksPerSeg)
#define MAX_BLKADDR(sbi)    (SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi))

#define START_BLOCK(sbi, segno)    (SM_I(sbi)->mainBlkaddr +        \
    ((segno) << sbi->logBlksPerSeg))

#define NEXT_FREE_BLKADDR(sbi, curseg)                    \
    (START_BLOCK(sbi, (curseg)->segNum) + (curseg)->nextBlkOffset)

#define SIT_BLK_CNT(sbi)                        \
    ((MAIN_SEGS(sbi) + SIT_ENTRIES_PER_BLOCK - 1) / SIT_ENTRIES_PER_BLOCK)

inline struct CurSegmentInfo *CURSEG_I(std::shared_ptr<HmfsSbInfo> sbi, int type)
{
    return (struct CurSegmentInfo *)(SM_I(sbi)->curSegmentArray + type);
}

/* for the list of fsync inodes, used only during recovery */
struct FsyncInodeEntry {
    struct ListNode list;    /* list head */
    uint32_t ino;        /* inode number */
    uint32_t blkaddr;    /* block address locating the last fsync */
    uint32_t last_dentry;    /* block address locating the last dentry */
};

#define NatsInCursum(jnl)             (LE16_TO_NATIVE(jnl->nNats))
#define SitsInCursum(jnl)             (LE16_TO_NATIVE(jnl->nSits))

#define NatInJournal(jnl, i)          (jnl->natJ.entries[i].ne)
#define NidInJournal(jnl, i)          (jnl->natJ.entries[i].nid)
#define SitInJournal(jnl, i)          (jnl->sitJ.entries[i].se)
#define SegnoInJournal(jnl, i)        (jnl->sitJ.entries[i].segno)

#define SIT_ENTRY_OFFSET(sit_i, segno)                                  \
    ((segno) % sit_i->sentsPerBlock)
#define SIT_BLOCK_OFFSET(sit_i, segno)                                  \
    ((segno) / SIT_ENTRIES_PER_BLOCK)

inline bool IsValidDataBlkAddr(uint32_t blkaddr)
{
    if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR || blkaddr == COMPRESS_ADDR) {
        return false;
    }
    return true;
}

inline int32_t IS_CUR_SEGNO(std::shared_ptr<HmfsSbInfo> sbi, uint32_t segno)
{
    if (sbi == nullptr) {
        return -1;
    }
    for (int32_t i = 0; i < NO_CHECK_TYPE; i++) {
        struct CurSegmentInfo *curseg = CURSEG_I(sbi, i);
        if (segno == curseg->segNum) {
            return 1;
        }
    }
    return 0;
}

// TODO 改成int32_t 来校验错误
inline uint64_t BLKOFF_FROM_MAIN(std::shared_ptr<HmfsSbInfo> sbi, uint64_t blkAddr)
{
    if (sbi == nullptr) {
        return 1;
    }
    ASSERT(blkAddr >= SM_I(sbi)->mainBlkaddr);
    return blkAddr - SM_I(sbi)->mainBlkaddr;
}

// TODO 改成int32_t 来校验错误
inline uint32_t GET_SEGNO(std::shared_ptr<HmfsSbInfo> sbi, uint64_t blkAddr)
{
    if (sbi == nullptr) {
        return 1;
    }
    return static_cast<uint32_t>(BLKOFF_FROM_MAIN(sbi, blkAddr) >> sbi->logBlksPerSeg);
}

inline uint32_t OFFSET_IN_SEG(std::shared_ptr<HmfsSbInfo> sbi, uint64_t blkAddr)
{
    return (uint32_t)(BLKOFF_FROM_MAIN(sbi, blkAddr)
            % (1 << sbi->logBlksPerSeg));
}

} // namespace Hmfs
} // namespace OHOS
#endif /* HMFS_RESIZE_DATA_H */
